package fr.cephb.joperon.tool.createprj;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;



import com.sleepycat.db.DatabaseException;
import com.sleepycat.db.OperationStatus;

import fr.cephb.joperon.core.Fid;
import fr.cephb.joperon.core.Mode;
import fr.cephb.joperon.core.Ymd;
import fr.cephb.joperon.core.bio.Assembly;
import fr.cephb.joperon.core.db.PrjFinDB;
import fr.cephb.joperon.core.db.PrjSnpDB;
import fr.cephb.joperon.core.entities.PrjFin;
import fr.cephb.joperon.core.entities.PrjSnp;
import fr.cephb.joperon.tool.OperonFactory;
import fr.cephb.joperon.webapp.jpa.User;

public class CreatePrjFin
	{
	private Pattern RS_PATTERN= Pattern.compile("rs[0-9]+",Pattern.CASE_INSENSITIVE);
	private OperonFactory factory=null;
	private User user=null;
	private String projectId=null;
	private File inputFile=null;
	private Assembly assembly=null;
	private String def="";
	private PrjFin.Target target=null;
	public CreatePrjFin()
		{
		
		}
	
	public void setTarget(PrjFin.Target target) {
		this.target = target;
		}
	
	public void setFactory(OperonFactory factory) {
		this.factory = factory;
	}
	public void setUser(User user) {
		this.user = user;
	}
	public void setProjectId(String projectId) {
		this.projectId = projectId;
	}
	public void setInputFile(File inputFile) {
		this.inputFile = inputFile;
	}
	public void setAssembly(Assembly assembly) {
		this.assembly = assembly;
	}
	public void setDef(String def) {
		this.def = def;
	}
	
	
	
	
	public PrjFin build() throws DatabaseException,IOException
		{
		if(projectId==null) throw new DatabaseException("pid not defined");
		if(assembly==null) throw new DatabaseException("assembly not defined");
		if(factory==null) throw new DatabaseException("factory not defined");
		if(def==null) throw new DatabaseException("def not defined");
		if(user==null) throw new DatabaseException("user not defined");
		if(inputFile==null) throw new DatabaseException("inputFile not defined");
		if(!user.canWriteProject(this.projectId)) throw new DatabaseException("user cannot write this project.");
		if(factory.getOperon().getMode()==Mode.READ_ONLY) throw new DatabaseException("operon is read only");
		if(target==null) throw new DatabaseException("target not defined");
		if(target!=PrjFin.Target.SNP) throw new DatabaseException("target not supported");
		
		
		if(factory.getPrjMgr().get(projectId)==null)  throw new DatabaseException("project does not exists");
		if(!factory.isPrjSnpExists(this.assembly)) throw new DatabaseException("db for this assembly does not exist");
		
		PrjFin prjfin=null;
		PrjFinDB db=null;
		PrjSnpDB db2=null;
		int count=0;
		
		for(int loop=0;loop<2;++loop)
			{
			if(loop==1)
				{
				prjfin= new PrjFin();
				prjfin.noNullString();
				prjfin.setPid(this.projectId);
				prjfin.setDef(this.def+" count:"+count);
				prjfin.setDoe(Ymd.now());
				prjfin.setLog(this.user.getLogin());
				prjfin.setYmd(Ymd.now());
				prjfin.setAssembly(this.assembly);
				
				
				
				db= this.factory.getPrjFin();
				prjfin.setFix(db.nextFix(this.projectId,PrjFin.Target.SNP));
				if(db.put(this.projectId, prjfin)!=OperationStatus.SUCCESS)
					{
					throw new DatabaseException("cannot insert "+prjfin.getProjCode());
					}
				
				db2=this.factory.getPrjSnp(this.assembly);
				}
			String line;
			BufferedReader in= null;
			try
				{
				in=new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(inputFile))));
				}
			catch(IOException err)
				{
				in=new BufferedReader(new FileReader(inputFile));
				}
			while((line=in.readLine())!=null)
				{
				if(line.startsWith("#") || line.trim().length()==0) continue;
				line=line.trim();
				if(        line.equalsIgnoreCase("snp")
						|| line.equalsIgnoreCase("rs")
						|| line.equalsIgnoreCase("name")) continue;
				
				if(RS_PATTERN.matcher(line).matches())
					{
					line=line.toLowerCase();
					}
				
				List<Fid> fids=factory.getSnpIds(this.assembly).getFids(line);
				if(fids.isEmpty())
					{
					throw new IOException("Cannot find FID for "+line+" in "+this.assembly);
					}
				if(loop==1)
					{
					for(Fid fid:fids)
						{
						String fin=this.projectId+"_"+prjfin.getFix();
						PrjSnp snp= new PrjSnp();
						snp.noNullString();
						snp.setAcn(line);
						snp.setFin(fin);
						snp.setFid(fid);
						if(db2.put(fin,snp)!=OperationStatus.SUCCESS)
							{
							throw new DatabaseException("cannot insert "+snp.getAcn()+" in "+fin);
							}
						}
					}
				else
					{
					count+= fids.size();
					}
				}
			in.close();
			}
			
		return prjfin;
		}
	
	}
